package com.hisense.ovcloud.warehouse.routers;

import com.hisense.ovcloud.commons.configs.base.BaseResponse;
import com.hisense.ovcloud.commons.configs.validator.BaseRouterConfiguration;
import com.hisense.ovcloud.warehouse.dto.req.*;
import com.hisense.ovcloud.warehouse.dto.resp.AuthBatchResp;
import com.hisense.ovcloud.warehouse.dto.resp.AuthTokenAndBatchResp;
import com.hisense.ovcloud.warehouse.dto.resp.CipherWithDKeyResp;
import com.hisense.ovcloud.warehouse.service.DevicesService;
import com.hisense.ovcloud.warehouse.service.WarehouseService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springdoc.core.annotations.RouterOperation;
import org.springdoc.core.annotations.RouterOperations;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.SynchronousSink;

import javax.annotation.PostConstruct;
import java.util.function.BiConsumer;

import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;


@Configuration
public class WarehouseRoutersConfiguration extends BaseRouterConfiguration {

    private final WarehouseService warehouseService;

    private final DevicesService devicesService;


    private final Validator activateDeviceReqValidator;
    private final Validator cipherWithDKeyReqValidator;
    private final Validator authBatchReqValidator;
    private final Validator registerDevicesReqValidator;
    private final Validator batchCodeCountReqValidator;

    private final Validator getCipheredDKeyReqValidator;

    private final Validator authTokenAndBatchReqValidator;

    private BiConsumer<ActivateDeviceReq, SynchronousSink<ActivateDeviceReq>> activateDeviceReqValidatorHandler;
    private BiConsumer<AuthBatchReq, SynchronousSink<AuthBatchReq>> authBatchReqValidatorHandler;
    private BiConsumer<CipherWithDKeyReq, SynchronousSink<CipherWithDKeyReq>> cipherWithDKeyReqValidatorHandler;

    private BiConsumer<CipherWithDKeyReq, SynchronousSink<CipherWithDKeyReq>> getCipheredDKeyReqValidatorHandler;
    private BiConsumer<RegisterDevicesReq, SynchronousSink<RegisterDevicesReq>> registerDevicesReqValidatorHandler;
    private BiConsumer<BatchCodeCountReq, SynchronousSink<BatchCodeCountReq>> batchCodeCountReqValidatorHandler;

    private BiConsumer<AuthTokenAndBatchReq, SynchronousSink<AuthTokenAndBatchReq>> authTokenAndBatchReqValidatorHandler;

    @PostConstruct
    public void init() {
        activateDeviceReqValidatorHandler = (body, sink) -> validate(sink, activateDeviceReqValidator, body, ActivateDeviceReq.class);
        authBatchReqValidatorHandler = (body, sink) -> validate(sink, authBatchReqValidator, body, AuthBatchReq.class);
        getCipheredDKeyReqValidatorHandler = (body, sink) -> validate(sink, getCipheredDKeyReqValidator, body, CipherWithDKeyReq.class);
        cipherWithDKeyReqValidatorHandler = (body, sink) -> validate(sink, cipherWithDKeyReqValidator, body, CipherWithDKeyReq.class);
        registerDevicesReqValidatorHandler = (body, sink) -> validate(sink, registerDevicesReqValidator, body, RegisterDevicesReq.class);
        batchCodeCountReqValidatorHandler = (body, sink) -> validate(sink, batchCodeCountReqValidator, body, BatchCodeCountReq.class);
        authTokenAndBatchReqValidatorHandler = (body, sink) -> validate(sink, authTokenAndBatchReqValidator, body, AuthTokenAndBatchReq.class);
    }

    public WarehouseRoutersConfiguration(WarehouseService warehouseService, DevicesService devicesService, @Qualifier("activateDeviceReqValidator") Validator activateDeviceReqValidator, @Qualifier("cipherWithDKeyReqValidator") Validator cipherWithDKeyReqValidator, @Qualifier("authBatchReqValidator") Validator authBatchReqValidator, @Qualifier("registerDevicesReqValidator") Validator registerDevicesReqValidator, @Qualifier("batchCodeCountReqValidator") Validator batchCodeCountReqValidator, @Qualifier("getCipheredDKeyReqValidator") Validator getCipheredDKeyReqValidator, @Qualifier("authTokenAndBatchReqValidator") Validator authTokenAndBatchReqValidator) {
        this.warehouseService = warehouseService;
        this.devicesService = devicesService;
        this.activateDeviceReqValidator = activateDeviceReqValidator;
        this.cipherWithDKeyReqValidator = cipherWithDKeyReqValidator;
        this.authBatchReqValidator = authBatchReqValidator;
        this.registerDevicesReqValidator = registerDevicesReqValidator;
        this.batchCodeCountReqValidator = batchCodeCountReqValidator;
        this.getCipheredDKeyReqValidator = getCipheredDKeyReqValidator;
        this.authTokenAndBatchReqValidator = authTokenAndBatchReqValidator;
    }

    @Bean
    @RouterOperations({
            @RouterOperation(
                    path = "/ac-julink-warehouse/createSqlTable",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            summary = "createSqlTable",
                            operationId = "createSqlTable",
                            description = "createSqlTable",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "CreateSqlReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = CreateSqlReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/authBatch",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "authBatch",
                            summary = "called by DMS to check whether the device is registered",
                            description = "authBatch",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "AuthBatchReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = AuthBatchReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/cipherWithDKey",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "cipherWithDKey",
                            description = "cipherWithDKey",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "CipherWithDKeyReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = CipherWithDKeyReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/getCipheredDKey",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "getCipheredDKey",
                            description = "getCipheredDKey",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "CipherWithDKeyReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = CipherWithDKeyReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/activateDevice",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "activateDevice",
                            description = "activateDevice",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "ActivateDeviceReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = ActivateDeviceReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/registerDevices",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "registerDevices",
                            description = "registerDevices",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "RegisterDevicesReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = RegisterDevicesReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/getPublicKey",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "getPublicKey",
                            description = "getPublicKey",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "ActivateDeviceReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = ActivateDeviceReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            ),
            @RouterOperation(
                    path = "/ac-julink-warehouse/checkBatchCodeCount",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "checkBatchCodeCount",
                            description = "checkBatchCodeCount",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "BatchCodeCountReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = BatchCodeCountReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = BaseResponse.class)))
                            }
                    )
            )
    })
    public RouterFunction<ServerResponse> warehouseRoutes() {
        return RouterFunctions.nest(path("/ac-julink-warehouse"),
                route()
                        .POST("/createSqlTable", req -> ServerResponse.ok().body(devicesService.createSqlTable(req.bodyToMono(CreateSqlReq.class)),
                                BaseResponse.class))
                        .POST("/authBatch", req -> ServerResponse.ok().body(devicesService.authBatch(req.bodyToMono(AuthBatchReq.class).handle(authBatchReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/cipherWithDKey", req -> ServerResponse.ok().body(warehouseService.cipherWithDKey(req.bodyToMono(CipherWithDKeyReq.class).handle(cipherWithDKeyReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/getCipheredDKey", req -> ServerResponse.ok().body(warehouseService.getCipheredDKey(req.bodyToMono(CipherWithDKeyReq.class).handle(getCipheredDKeyReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/activateDevice", req -> ServerResponse.ok().body(devicesService.checkAndActivateDeviceThenCreateToken(req.bodyToMono(ActivateDeviceReq.class).handle(activateDeviceReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/registerDevices", req -> ServerResponse.ok().body(devicesService.registerDevices(req.bodyToMono(RegisterDevicesReq.class).handle(registerDevicesReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/getPublicKey", req -> ServerResponse.ok().body(devicesService.getPublicKey(req.bodyToMono(ActivateDeviceReq.class).handle(activateDeviceReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/checkBatchCodeCount", req -> ServerResponse.ok().body(devicesService.checkBatchCodeCount(req.bodyToMono(BatchCodeCountReq.class).handle(batchCodeCountReqValidatorHandler)),
                                BaseResponse.class))
                        .POST("/migrate", req -> ServerResponse.ok().body(devicesService.migrate(req.bodyToMono(MigrateReq.class)),
                                BaseResponse.class))
                        .POST("/pressDataCreate", req -> ServerResponse.ok().body(devicesService.pressDataCreate(req.bodyToMono(PressDataCreateReq.class)),
                                BaseResponse.class))
                        .build());
    }

    @RouterOperations({
            @RouterOperation(
                    path = "/zc-julink-warehouse/v1/cipherWithDKey",
                    method = RequestMethod.POST,
                    produces = "application/json",
                    operation = @Operation(
                            operationId = "cipherWithDKey",
                            description = "cipherWithDKey",
                            requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
                                    description = "CipherWithDKeyReq",
                                    required = true,
                                    content = {
                                            @Content(
                                                    mediaType = "application/json",
                                                    schema = @Schema(implementation = CipherWithDKeyReq.class)
                                            )
                                    }
                            ),
                            responses = {
                                    @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = CipherWithDKeyResp.class)))
                            }
                    )
            )
    })
    @Bean
    public RouterFunction<ServerResponse> warehouseHimitRoutes() {
        return RouterFunctions.nest(path("/zc-julink-warehouse/v1"),
                route()
                        .POST("/cipherWithDKey", req -> ServerResponse.ok().body(warehouseService.cipherWithDKeyBase(req.bodyToMono(CipherWithDKeyReq.class).handle(cipherWithDKeyReqValidatorHandler)),
                                CipherWithDKeyResp.class))
                        .POST("/authBatch", req -> ServerResponse.ok().body(devicesService.authBatchOrigin(req.bodyToMono(AuthBatchReq.class).handle(authBatchReqValidatorHandler)),
                                AuthBatchResp.class))
                        .POST("/authTokenAndBatch", req -> ServerResponse.ok().body(devicesService.authTokenAndBatch(req.bodyToMono(AuthTokenAndBatchReq.class).handle(authTokenAndBatchReqValidatorHandler)),
                                AuthTokenAndBatchResp.class))
                        .build());
    }
}

